#include <stdio.h>
#include <unistd.h>
#include <limits.h>

#include "Scanner.h"
#include "Token.h"
#include "Information.h"
#include "../Symtable/SymInfo.h"

ScannerImp::ScannerImp(Buffer &newBuffer) :
		buffer(newBuffer), infoStorage(65536), lexemStorage(65536), line(1), column(
				0), prevColumn(0) {
	automat = new AutomatOO(this);
	symTable = new SymTable(65536);
	static char intType [5] = {0};
	// TODO: use strcpy
	intType[0] = 3;
	intType[1] = 'i';
	intType[2] = 'n';
	intType[3] = 't';
	symTable->insert(intType, INT_TYPE);
}

ScannerImp::~ScannerImp() {
	delete automat;
	puts("scanner delete1");
	delete symTable;
	puts("scanner delete2");
}

bool ScannerImp::nextToken(Token& token) {
	tokenLenght = 0;
	char cc[2] = {0};

	for (type = NONE; type == NONE || type == SKIP; type = automat->handle(c)) {
		c = buffer.getChar();
		cc[0] = c;
		//fputs(cc, stdout);
		increaseRowLineCounter(c);
		if (type == NONE) {
			++tokenLenght;
		}
	}
	puts("");

	mkToken(type, &token);
	return (type != END_OF_FILE);
}

void ScannerImp::freeToken(Token* t) {
	// not used
}

void ScannerImp::mkToken(TokenType tt, Token* t) {
	if (tt == END_OF_FILE) {
		t->setType(tt);
		t->setLine(line);
		t->setColumn(column);
		t->setInformation(0);
		return;
	}

	static void* info;
	static char* lexem;

	if (tokenLenght) {
		lexem = lexemStorage.allocSpace(tokenLenght + 1);
		buffer.copyString(tokenLenght, lexem);
	}

	switch (tt) {
	case INTEGER:
		info = infoStorage.allocSpace(sizeof(InformationInteger));
		((InformationInteger*) info)->lexem = lexem;
		{
			int number = 0;

			for (size_t i = 1; i <= tokenLenght; ++i) {
				if (number * 10 + 9 > INT_MAX)
				{
					tt = ERROR;
					break;
				}
				number *= 10;
				number += (lexem[i] - '0');
			}
			((InformationInteger*) info)->number = number;
		}
		break;
	case COMMENT:
		info = infoStorage.allocSpace(sizeof(InformationComment));
		((InformationComment*) info)->lexem = lexem;
		break;
	case ERROR:
		info = infoStorage.allocSpace(sizeof(InformationError));
		((InformationError*) info)->lexem = lexem;
		break;
	default:
		break;
	}

	t->setType(tt);
	t->setLine(line);
	t->setColumn(column);

	if (tt == IDENTIFIER || tt == RESERVED_WORD) {
		// check Symbol table here
		t->setInformation((void*)(symTable->lookup(symTable->insert(lexem, tt))));
		tt = ((SymInfo*)(t->getInformation()))->type;

	} else {
		t->setInformation(info);
	}
	t->setType(tt);
}

inline void ScannerImp::increaseRowLineCounter(char c) {
	if (c == '\n') {
		prevColumn = column;
		++line;
		column = 0;
	} else {
		++column;
	}
}

inline void ScannerImp::decreaseRowLineCounter() {
	if (column > 1) {
		--column;
	} else {
		column = prevColumn;
		if (line > 0) {
			--line;
		}
	}
	if (tokenLenght) {
		--tokenLenght;
	}
}

void ScannerImp::ungetChar(size_t i) {
	while (i > 0) {
		decreaseRowLineCounter();
		buffer.ungetChar();
		--i;
	}
}

void ScannerImp::stop() {
	// not used
}

